// Author: cxx
// GitHub: https://github.com/ccolorcat

import 'dart:ui' as ui show Codec, ImmutableBuffer;

import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';

import '../finder.dart' hide ProgressListener;
import 'uri_loader.dart';

class FinderImage extends ImageProvider<FinderImage> {
  const FinderImage(
      this.uri, {
        this.scale = 1.0,
        this.headers,
        this.listener,
      });

  FinderImage.withAsset({
    required String assetName,
    String? packageName,
    this.scale = 1.0,
  })  : uri = withAsset(assetName: assetName, packageName: packageName),
        headers = null,
        listener = null;

  final Uri uri;
  final double scale;
  final Map<String, Object>? headers;
  final ProgressListener? listener;

  @override
  Future<FinderImage> obtainKey(ImageConfiguration configuration) {
    return SynchronousFuture<FinderImage>(this);
  }

  @override
  ImageStreamCompleter loadImage(FinderImage key, ImageDecoderCallback decode) {
    return MultiFrameImageStreamCompleter(
      codec: _loadAsync(key, decode),
      scale: key.scale,
      debugLabel: key.uri.toString(),
      informationCollector: () sync* {
        yield ErrorDescription('uri: $uri');
      },
    );
  }

  Future<ui.Codec> _loadAsync(
      FinderImage key, ImageDecoderCallback decode) async {
    try {
      assert(key == this);
      print('Loading image: ${key.uri}');
      final bytes = await Finder().getData(
        key.uri,
        headers: headers,
        listener: listener,
      );
      print('Image loaded, byte length: ${bytes.lengthInBytes}');
      if (bytes.lengthInBytes == 0) {
        // 该文件可能稍后可用
        PaintingBinding.instance.imageCache.evict(key);
        throw StateError('$uri cannot be loaded as an image.');
      }

      final buffer = await ui.ImmutableBuffer.fromUint8List(bytes);
      return await decode(buffer);
    } catch (e, stacktrace) {
      print('zhangsl---Error loading image: $e');
      print(stacktrace);
      rethrow;
    }
  }

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
          other is FinderImage &&
              runtimeType == other.runtimeType &&
              uri == other.uri &&
              scale == other.scale;

  @override
  int get hashCode => uri.hashCode ^ scale.hashCode;

  @override
  String toString() {
    return 'FinderImage{uri: $uri, scale: $scale, headers: $headers, listener: $listener}';
  }
}
